package com.lin.aspect;

import com.lin.annotation.MyLog;
import com.lin.pojo.Log;
import com.lin.pojo.User;
import com.lin.services.LogServices;
import com.lin.utils.IPUtil;
import com.lin.utils.ShiroUtil;
import com.lin.utils.WebUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;

/**
 * 自定义日志注解切面处理类
 */
@Aspect
@Component
public class MyLogAspect {

    @Autowired
    private LogServices logServices;

    @Pointcut("@annotation(com.lin.annotation.MyLog)")
    public void logPointCut() {
    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
//        方法执行前的shiro登录用户
        User beforeUser = ShiroUtil.getUser();
        MethodSignature signature = (MethodSignature) point.getSignature();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        Method method = signature.getMethod();
        Log log = new Log();
        MyLog myLog = method.getAnnotation(MyLog.class);
        long beginTime = System.currentTimeMillis();
        //执行方法
        Object ret = point.proceed();
        //执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        log.setTime(time + 1L);
        if (myLog != null) {
            //注解的值
            log.setOperation(myLog.value());
        }
        //请求的类名，方法名
        String className = point.getTarget().getClass().getName();
        String methodName = signature.getName();
        log.setMethod(className + "." + methodName + "()");
        String retType = point.getSignature().toLongString().split(" ")[1];
//        权限不足时不会执行@MyLog注解。。多个AOP注解可以在定义注解是加上@order改变执行顺序
        //方法执行后的shiro登录用户
        User afterUser = ShiroUtil.getUser();
        if (afterUser == null) {
            // 方法执行后，当前用户为空，使用方法执行前获取用户，退出登录
            log.setUserId(beforeUser.getId());
            log.setUsername(beforeUser.getName());
            log.setStoreId(beforeUser.getStoreId());
            log.setUserAccountNumber(beforeUser.getAccountNumber());
        } else {
            // 方法执行后，当前用户不为空，使用方法执行后用户，登陆成功
            log.setUserId(afterUser.getId());
            log.setUsername(afterUser.getName());
            log.setStoreId(afterUser.getStoreId());
            log.setUserAccountNumber(afterUser.getAccountNumber());
        }
        String remoteIP = IPUtil.getIpAddress(request);
        log.setIp(remoteIP);
        //保存系统日志 暂时关闭
        logServices.insertLog(log);
        return ret;
    }

//    @Around("logPointCut()")
//    public Object around(ProceedingJoinPoint point) throws Throwable {
//        User currUser = ShiroUtil.getUser();
//        long beginTime = System.currentTimeMillis();
//        //执行方法
//        Object result = point.proceed();
//        //执行时长(毫秒)
//        long time = System.currentTimeMillis() - beginTime;
//        //保存日志
//        saveSysLog(point, time, currUser);
//        return result;
//    }

    private void saveSysLog(ProceedingJoinPoint joinPoint, long time, User userEntity) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log sysLog = new Log();
        MyLog syslog = method.getAnnotation(MyLog.class);
        if (syslog != null) {
            //注解上的描述
            sysLog.setOperation(syslog.value());
        }
        //请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        sysLog.setMethod(className + "." + methodName + "()");
        //请求的参数
//        Object[] args = joinPoint.getArgs();
//        try {
//            String params = JSONUtils.beanToJson(args[0]);
//            sysLog.setParams(params);
//        } catch (Exception e) {
//
//        }
        //获取request
        HttpServletRequest request = WebUtil.getRequest();
        //设置IP地址
        sysLog.setIp(IPUtil.getIpAddress(request));
        //用户名
        User currUser = ShiroUtil.getUser();
        if (currUser != null) {
            // 方法执行后，当前用户为空，使用方法执行前获取用户，登出操作
            sysLog.setUserId(userEntity.getId());
            sysLog.setUsername(userEntity.getAccountNumber());
        } else {
            // 方法执行后，当前用户不为空，使用方法执行后用户，登录操作
            sysLog.setUserId(currUser.getId());
            sysLog.setUsername(currUser.getAccountNumber());
        }
        sysLog.setTime(time);
        //保存系统日志
        logServices.insertLog(sysLog);
    }

    private void insertLog(ProceedingJoinPoint joinPoint, long time, User user) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log log = new Log();
        MyLog myLog = method.getAnnotation(MyLog.class);
        if (myLog != null) {
            //注解的值
            log.setOperation(myLog.value());
        }
        //请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        log.setMethod(className + "." + methodName + "()");
        //用户名
        User currUser = ShiroUtil.getUser();
        if (currUser == null) {
            // 方法执行后，当前用户为空，使用方法执行前获取用户，登出操作
            log.setUserId(user.getId());
            log.setUsername(user.getName());
        } else {
            // 方法执行后，当前用户不为空，使用方法执行后用户，登录操作
            log.setUserId(currUser.getId());
            log.setUsername(currUser.getName());
        }
        log.setTime(time);
        //保存系统日志
        logServices.insertLog(log);
    }

    public static void alterAnnotationOn(Method method, String newValue) throws Exception {
        method.setAccessible(true);
        boolean methodHasAnnotation = method.isAnnotationPresent(MyLog.class); // 是否指定类型的注释存在于此元素上
        if (methodHasAnnotation) {
            // 得到注解
            MyLog myLog = method.getAnnotation(MyLog.class);
            // 修改
            String value = myLog.value();
            InvocationHandler invocationHandler = Proxy.getInvocationHandler(myLog);
            // annotation注解的membervalues
            Field hField = invocationHandler.getClass().getDeclaredField("memberValues");
            // 因为这个字段是 private final 修饰，所以要打开权限
            hField.setAccessible(true);
            // 获取 memberValues
            Map<String, String> memberValues = (Map) hField.get(invocationHandler);
            // 修改 description属性值
            memberValues.put("value", newValue);
        }
    }

    //       mylog参数修改设置
//        try {
//        // 获取当前线程的方法名
//        String name = Thread.currentThread().getStackTrace()[1].getMethodName();
//        // getMethod里面的参数对应updateUserInfo方法的参数，固定形式的，不可少
//        Method method = CategoryController.class.getMethod(name, HttpServletRequest.class);
//        // 描述信息
//        String newValue = "添加分类:" + Category1Context;
//        // 调用修改方法
//        MyLogAspect.alterAnnotationOn(method, newValue);
//    } catch (Exception e) {
//        e.printStackTrace();
//    }

}
